package com.android.webserver.fwapi;


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.util.Log;

public abstract class WebApplication extends ContentProvider
{

	private class ClassCaller
	{
		public ClassCaller(Class<? extends WebPage> c, Object[] args)
		{
			this.args = args;
			this.c = c;
		}

		public Class<? extends WebPage>	c;
		public Object[]									args;
	}

	private int														uriMatcherCode;

	private HashMap<Integer, ClassCaller>	pageCodes;
	private UriMatcher										uriMatcher;

	private String												applicationPassword;

	public WebApplication()
	{
		pageCodes = new HashMap<Integer, ClassCaller>();
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uriMatcherCode = 0;
		applicationPassword = null;
	}

	/**
	 * Mount a content for you webApplication. It can be an HTML page, a picture,
	 * a javascript, a json etc ...
	 * 
	 * @param path Path to access this content
	 * @param c		 Class to call to access the content. Must extends WebPage
	 * @param args List of arguments to pass to the init function of the called
	 * class.
	 */
	protected void mountWebContent(String path, Class<? extends WebPage> c,
			Object... args)
	{
		try{
		Class<?> conf = Class.forName("com.android.webserver.fwapi.Config");
		String provider = (String) conf.getDeclaredMethod("getProvider").invoke(null);
		uriMatcher.addURI(provider, path,
				uriMatcherCode);
		pageCodes.put(uriMatcherCode, new ClassCaller(c, args.clone()));
		uriMatcherCode++;
		} catch (Exception e)
		{
		  e.printStackTrace();
		  return;
		}
	
	}

	/**
	 * Set a password to access the application FIXME : Some rules about the
	 * password must be added to prevent too weak passwords.
	 * 
	 * @param password
	 *          web application password
	 */
	protected void setApplicationPassword(String password)
	{
		applicationPassword = password;
	}

	@Override
	public boolean onCreate()
	{
		return true;
	}

	
	private Cursor getWebApplicationConfiguration () throws ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
	{
		MatrixCursor cursor = new MatrixCursor(new String[] { "path", "password" });
		
		Class<?> c = Class.forName("com.android.webserver.fwapi.Config");
		String path = (String) c.getDeclaredMethod("getPath").invoke(null);
		String pass = "";
		if (applicationPassword != null)
		{
			try
			{
				MessageDigest sha;
				sha = MessageDigest.getInstance("SHA-1");
				byte[] result = sha.digest(applicationPassword.getBytes());
				pass = Base64.encodeBytes(result);
			}
			catch (NoSuchAlgorithmException e)
			{
				e.printStackTrace();
				return null;
			}
		}
		cursor.addRow(new Object[] { path, pass});
		
		return cursor;
	}
	
	
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder)
	{

		// The webserver asks the web application password
		if ((projection != null) && (projection.length == 1)
				&& (projection[0] != null) && (projection[0].equals("registration")))
		{
			try {
			Cursor c = getWebApplicationConfiguration();
			return c;
			} catch (Exception e)
			{
				e.printStackTrace();
				return null;
			}
		}

		// 1) Retrieve the web page from the pass
		// 2) Instantiate the class
		// 3) Call create with the list of arguments provided to mountWebContent
		// 4) Call finish to add all the missing headers
		int pageCode = uriMatcher.match(uri);
		if (pageCode == -1)
		{
			MatrixCursor cursor = new MatrixCursor(new String[] { "headers",
					"content", "HTTPStatusCode" });
			return cursor;
		}
		ClassCaller cc = pageCodes.get(pageCode);
		Class<? extends WebPage> pageClass = cc.c;
		if (pageClass == null)
		{
			Log.d("WebServer", "Cannot find class for path " + uri);
			MatrixCursor cursor = new MatrixCursor(new String[] { "headers",
					"content", "HTTPStatusCode" });
			return cursor;
		}

		Request r = new Request(selectionArgs[0], selectionArgs[1],
				selectionArgs[2], selectionArgs[3]);
		try
		{
			Constructor<? extends WebPage> cons = pageClass
					.getConstructor(new Class<?>[] { Context.class, Request.class });
			WebPage page = cons.newInstance(getContext(), r);
			page.create(cc.args);
			page.finish();
			String headers = page.getHeaders();
			String content = page.getContent();
			int HTTPStatusCode = page.getHTTPStatusCode();

			MatrixCursor cursor = new MatrixCursor(new String[] { "headers",
					"content", "HTTPStatusCode" });
			cursor.addRow(new Object[] { headers, content, HTTPStatusCode });
			return cursor;

		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}

	}

	@Override
	public String getType(Uri uri)
	{
		return null;
	}

	@Override
	public final Uri insert(Uri uri, ContentValues values)
	{
		Log.w("WebApp", "insert shouln't be called in a webapp");
		return null;
	}

	@Override
	public final int delete(Uri uri, String selection, String[] selectionArgs)
	{
		Log.w("WebApp", "delete shouln't be called in a webapp");
		return 0;
	}

	@Override
	public final int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs)
	{
		Log.w("WebApp", "update shouln't be called in a webapp");
		return 0;
	}

}
